﻿using Core.Language;
using Core.Utility.Helper;
using RulesEngine.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Core.ValidateRules.Lib
{
    /// <summary>
    /// 枚举数据类型，规则引擎基础验证属性
    /// </summary>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
    public class ValidateRulesEnumDataTypeAttribute : DataTypeAttribute
    {
        /// <summary>
        /// 本地化错误信息字符串
        /// </summary>
        protected string LocalizeErrorMessageString
        {
            get
            {
                if (!string.IsNullOrEmpty(ErrorMessageString))
                {
                    return LanguageResource.GetResourceValue(ErrorMessageString).GetValueOrDefault();
                }
                else
                {
                    return ErrorMessageString;
                }
            }
        }

        public ValidateRulesEnumDataTypeAttribute(Type enumType)
            : base("Enumeration")
        {
            EnumType = enumType;
        }

        public Type EnumType { get; }

        public override bool IsValid(object? value)
        {
            if (EnumType == null)
            {
                throw new InvalidOperationException(LanguageResource.GetResourceValue("EnumDataTypeAttribute类型不能为NULL"));
            }
            if (!EnumType.IsEnum)
            {
                throw new InvalidOperationException(LanguageResource.StringFormatArgs("类型需要是一个枚举 {1}",new object?[] { EnumType.FullName }));
            }

            if (value == null)
            {
                return true;
            }
            var stringValue = value as string;
            if (stringValue?.Length == 0)
            {
                return true;
            }

            Type valueType = value.GetType();
            if (valueType.IsEnum && EnumType != valueType)
            {
                // don't match a different enum that might map to the same underlying integer
                return false;
            }

            if (!valueType.IsValueType && valueType != typeof(string))
            {
                // non-value types cannot be converted
                return false;
            }

            if (valueType == typeof(bool) ||
                valueType == typeof(float) ||
                valueType == typeof(double) ||
                valueType == typeof(decimal) ||
                valueType == typeof(char))
            {
                // non-integral types cannot be converted
                return false;
            }

            object convertedValue;
            if (valueType.IsEnum)
            {
                Debug.Assert(valueType == value.GetType(), "The valueType should equal the Type of the value");
                convertedValue = value;
            }
            else
            {
                try
                {
                    convertedValue = stringValue != null
                        ? Enum.Parse(EnumType, stringValue, false)
                        : Enum.ToObject(EnumType, value);
                }
                catch (ArgumentException)
                {
                    // REVIEW: is there a better way to detect this
                    return false;
                }
            }

            if (IsEnumTypeInFlagsMode(EnumType))
            {
                // REVIEW: this seems to be the easiest way to ensure that the value is a valid flag combination
                // If it is, the string representation of the enum value will be something like "A, B", while
                // the string representation of the underlying value will be "3". If the enum value does not
                // match a valid flag combination, then it would also be something like "3".
                string underlying = GetUnderlyingTypeValueString(EnumType, convertedValue)!;
                string? converted = convertedValue.ToString();
                return !underlying.Equals(converted);
            }

            return Enum.IsDefined(EnumType, convertedValue);
        }

        private static bool IsEnumTypeInFlagsMode(Type enumType) =>
            enumType.IsDefined(typeof(FlagsAttribute), false);

        private static string? GetUnderlyingTypeValueString(Type enumType, object enumValue) =>
            Convert.ChangeType(enumValue, Enum.GetUnderlyingType(enumType), CultureInfo.InvariantCulture).ToString();

        /// <summary>
        /// 验证返回的最终结果，可通过该方法进行拦截，输出本地化后的对象
        /// </summary>
        /// <param name="value"></param>
        /// <param name="validationContext"></param>
        /// <returns></returns>
        protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
        {
            //拦截上下文对象中的DisplayName属性，做本地化转换
            // call overridden method.
            string localizeDisplayName = LanguageResource.GetResourceValue(validationContext.DisplayName).GetValueOrDefault();
            if (!IsValid(value))
            {
                string[]? memberNames = validationContext.MemberName is { } memberName
                    ? new[] { memberName }
                    : null;
                var result = new ValidationResult(string.Format(LocalizeErrorMessageString, localizeDisplayName), memberNames);
                return result;
            }
            return ValidationResult.Success;
        }
    }
}
